<template>
  <div class="universal-upload">
    <!-- 图片上传模式 -->
    <template v-if="isImageMode">
      <el-upload
          v-model:file-list="fileList"
          :accept="computedAccept"
          :action="action"
          :before-upload="beforeUpload"
          :disabled="disabled"
          :limit="computedLimit"
          :list-type="'picture-card'"
          :multiple="isMultiple"
          :on-error="handleError"
          :on-exceed="handleExceed"
          :on-preview="handlePreview"
          :on-remove="handleRemove"
          :on-success="handleSuccess"
          :class="{ disabled: isImageMode && props.limit === 1 && fileList.length >= 1 }"
      >
        <template #default>
          <el-icon class="upload-icon">
            <Plus/>
          </el-icon>
        </template>
        <template v-if="showTip" #tip>
          <div class="upload-tip">{{ uploadTip }}</div>
        </template>
      </el-upload>

      <!-- 图片预览 -->
      <el-image-viewer
          v-if="previewVisible"
          :initial-index="previewIndex"
          :url-list="previewUrls"
          @close="previewVisible = false"
      />
    </template>

    <!-- 文件上传模式 -->
    <template v-else>
      <el-upload
          v-model:file-list="fileList"
          :accept="computedAccept"
          :action="action"
          :before-upload="beforeUpload"
          :disabled="disabled"
          :drag="drag"
          :limit="computedLimit"
          :multiple="isMultiple"
          :on-error="handleError"
          :on-exceed="handleExceed"
          :on-remove="handleRemove"
          :on-success="handleSuccess"
      >
        <template v-if="drag">
          <el-icon class="upload-drag-icon">
            <UploadFilled/>
          </el-icon>
          <div class="upload-drag-text">将文件拖到此处，或<em>点击上传</em></div>
        </template>
        <template v-else>
          <el-button type="primary">
            <el-icon>
              <Upload/>
            </el-icon>
            {{ uploadButtonText }}
          </el-button>
        </template>

        <template v-if="showTip" #tip>
          <div class="upload-tip">{{ uploadTip }}</div>
        </template>
      </el-upload>
    </template>
  </div>
</template>

<script setup>
import {computed, ref, watch} from 'vue'
import {ElMessage} from 'element-plus'
import {Plus, Upload, UploadFilled} from '@element-plus/icons-vue'
import {UPLOAD_URL} from "@/utils/config.js";

// Props定义
const props = defineProps({
  // 上传类型：image(单图片) | image-list(多图片) | file(单文件) | file-list(多文件)
  type: {
    type: String,
    default: 'file',
    validator: (value) => ['image', 'image-list', 'file', 'file-list'].includes(value)
  },

  // v-model绑定的值
  modelValue: {
    type: [Object, Array],
    default: () => null
  },

  // 上传接口地址
  action: {
    type: String,
    default: UPLOAD_URL
  },

  // 文件类型限制
  accept: {
    type: [String, Array],
    default: ''
  },

  // 文件数量限制
  limit: {
    type: Number,
    default: 1
  },

  // 文件大小限制(MB)
  maxSize: {
    type: Number,
    default: 10
  },

  // 是否禁用
  disabled: {
    type: Boolean,
    default: false
  },

  // 是否显示提示信息
  showTip: {
    type: Boolean,
    default: true
  },

  // 自定义提示信息
  tip: {
    type: String,
    default: ''
  },

  // 是否启用拖拽上传(仅文件模式)
  drag: {
    type: Boolean,
    default: false
  },

  // 上传按钮文字
  buttonText: {
    type: String,
    default: '选择文件'
  }
})

// Emits定义
const emit = defineEmits(['update:modelValue', 'success', 'error', 'change'])

// 响应式数据
const fileList = ref([])
const previewVisible = ref(false)
const previewIndex = ref(0)

// 预设文件类型
const FILE_TYPES = {
  image: 'image/*',
  video: 'video/*',
  audio: 'audio/*',
  document: '.pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx,.txt',
  archive: '.zip,.rar,.7z,.tar,.gz'
}

// 计算属性
const isImageMode = computed(() => props.type.includes('image'))
const isMultiple = computed(() => props.type.includes('-list'))

const computedAccept = computed(() => {
  if (props.accept) {
    if (Array.isArray(props.accept)) {
      return props.accept.map(type => FILE_TYPES[type] || type).join(',')
    }
    return FILE_TYPES[props.accept] || props.accept
  }
  return isImageMode.value ? FILE_TYPES.image : ''
})

const computedLimit = computed(() => {
  if (props.limit !== null) return props.limit
  return isMultiple.value ? 9 : 1
})

const uploadTip = computed(() => {
  if (props.tip) return props.tip

  const typeText = isImageMode.value ? '图片' : '文件'
  const limitText = computedLimit.value > 1 ? `最多${computedLimit.value}个` : '单个'
  const sizeText = `大小不超过${props.maxSize}MB`

  return `支持${limitText}${typeText}，${sizeText}`
})

const uploadButtonText = computed(() => {
  return props.buttonText || (isImageMode.value ? '选择图片' : '选择文件')
})

const previewUrls = computed(() => {
  return fileList.value
      .filter(file => file.url && isImageFile(file))
      .map(file => file.url)
})

// 工具函数
const isImageFile = (file) => {
  const imageTypes = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp']
  const extension = getFileExtension(file.name || file.url || '')
  return imageTypes.includes(extension.toLowerCase())
}

const getFileExtension = (filename) => {
  return filename.split('.').pop() || ''
}

// 转换数据格式 - 优化后支持后端JSON对象格式
const normalizeFileData = (data) => {
  if (!data) return []

  const files = Array.isArray(data) ? data : [data]
  return files.filter(Boolean).map(item => {
    // 如果是后端返回的文件对象格式 {name, filename, url, size}
    if (item && typeof item === 'object' && item.url) {
      return {
        uid: item.uid || `${Date.now()}-${Math.random()}`,
        name: item.name,
        filename: item.filename,
        url: item.url,
        status: 'success',
        size: item.size || 0,
      }
    }
    // 兼容旧的字符串URL格式
    else if (typeof item === 'string') {
      const fileName = item.split('/').pop() || 'file'
      return {
        uid: `${Date.now()}-${Math.random()}`,
        name: fileName,
        filename: fileName,
        url: item,
        status: 'success',
      }
    }
    return item
  })
}

// 提取文件数据 - 返回后端需要的格式
const extractFileData = (files) => {
  return isMultiple.value ? files : (files[0] || null)
}

// 事件处理 - 修复多文件上传问题
const handleSuccess = (response, uploadFile) => {
  const res = response.data

  if (res?.url) {
    // 找到对应的文件并更新
    const targetFile = fileList.value.find(file => file.uid === uploadFile.uid)
    if (targetFile) {
      targetFile.url = res.url
      targetFile.name = targetFile.name || res.name
      targetFile.filename = res.filename
      targetFile.status = 'success'
    }
  }

  emit('success', response, uploadFile)
  updateModelValue()
}

const handleError = (error, file) => {
  ElMessage.error('上传失败')
  emit('error', error, file)
}

const handleRemove = (file) => {
  // 从fileList中移除对应文件
  const index = fileList.value.findIndex(item => item.uid === file.uid)
  if (index !== -1) {
    fileList.value.splice(index, 1)
  }
  updateModelValue()
}

const handleExceed = () => {
  ElMessage.warning(`最多只能上传${computedLimit.value}个文件`)
}

const beforeUpload = (file) => {
  // 文件大小检查
  const isLtMaxSize = file.size / 1024 / 1024 < props.maxSize
  if (!isLtMaxSize) {
    ElMessage.error(`文件大小不能超过${props.maxSize}MB`)
    return false
  }

  // 文件类型检查
  if (computedAccept.value) {
    const acceptTypes = computedAccept.value.split(',')
    const fileType = file.type
    const fileName = file.name.toLowerCase()

    const isValidType = acceptTypes.some(type => {
      if (type.startsWith('.')) {
        return fileName.endsWith(type.toLowerCase())
      } else if (type.includes('/*')) {
        return fileType.startsWith(type.replace('/*', ''))
      } else {
        return fileType === type
      }
    })

    if (!isValidType) {
      ElMessage.error('文件类型不符合要求')
      return false
    }
  }

  return true
}

const handlePreview = (file) => {
  if (isImageMode.value && file.url) {
    const index = previewUrls.value.indexOf(file.url)
    previewIndex.value = Math.max(0, index)
    previewVisible.value = true
  }
}

const updateModelValue = () => {
  const newValue = extractFileData(fileList.value)
  emit('update:modelValue', newValue)
  emit('change', newValue)
}

// 监听modelValue变化
watch(
    () => props.modelValue,
    (newValue) => {
      const normalizedFiles = normalizeFileData(newValue)
      // 避免不必要的更新
      const currentFileData = extractFileData(fileList.value)
      if (JSON.stringify(currentFileData) !== JSON.stringify(newValue)) {
        fileList.value = normalizedFiles
      }
    },
    {immediate: true, deep: true}
)

// 暴露方法
defineExpose({
  clearFiles: () => {
    fileList.value = []
    updateModelValue()
  },
})
</script>

<style scoped>
.universal-upload {
  width: 100%;
}

.upload-icon {
  font-size: 28px;
  color: #8c939d;
  width: 28px;
  height: 28px;
}

.upload-drag-icon {
  font-size: 67px;
  color: #c0c4cc;
  margin: 40px 0 16px;
  line-height: 50px;
}

.upload-drag-text {
  color: #606266;
  font-size: 14px;
  text-align: center;
}

.upload-drag-text em {
  color: #409eff;
  font-style: normal;
}

.upload-tip {
  color: #606266;
  font-size: 12px;
  line-height: 1.2;
  margin-top: 7px;
}

:deep(.el-upload--picture-card) {
  --el-upload-picture-card-size: 104px;
}

:deep(.el-upload-list--picture-card .el-upload-list__item) {
  --el-upload-list-picture-card-size: 104px;
}

:deep .disabled .el-upload--picture-card {
  display: none !important;
}
</style>